﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;

namespace SignalrService.SignalrService
{
    public interface IOnlineClientManager
    {
        event EventHandler<OnlineClientEventArgs> ClientConnected;
        event EventHandler<OnlineClientEventArgs> ClientDisconnected;
        void Add(IOnlineClient client);
        bool Remove(string connectionId);

      
        IOnlineClient GetByConnectionId(string connectionId);
        IReadOnlyList<IOnlineClient> GetAllClients();
        IReadOnlyList<IOnlineClient> GetAllByUserId(long userId);

    }


    public class OnlineClientManager: IOnlineClientManager
    {
        public event EventHandler<OnlineClientEventArgs> ClientConnected;
        public event EventHandler<OnlineClientEventArgs> ClientDisconnected;
        protected ConcurrentDictionary<string, IOnlineClient> Clients { get; }
        protected readonly object SyncObj = new object();
        public OnlineClientManager()
        {
            Clients = new ConcurrentDictionary<string, IOnlineClient>();
        }
        public virtual void Add(IOnlineClient client)
        {
            lock (SyncObj)
            {
                var userWasAlreadyOnline = false;

                if (client != null)
                {
                    userWasAlreadyOnline = GetAllByUserId(client.UserId).Any();
                }

                Clients[client.ConnectionId] = client;
                ClientConnected?.Invoke(this, new OnlineClientEventArgs(client));
            }
        }
        public virtual bool Remove(string connectionId)
        {
            lock (SyncObj)
            {
                IOnlineClient client;
                var isRemoved = Clients.TryRemove(connectionId, out client);

                if (isRemoved)
                {
                    ClientDisconnected?.Invoke(this, new OnlineClientEventArgs(client));
                }

                return isRemoved;
            }
        }

        public virtual IOnlineClient GetByConnectionId(string connectionId)
        {
            lock (SyncObj)
            {
                IOnlineClient obj;
                if (Clients.TryGetValue(connectionId, out obj))
                {
                    return obj;
                }
                return null;
            }
        }

        public virtual IReadOnlyList<IOnlineClient> GetAllClients()
        {
            lock (SyncObj)
            {
                return Clients.Values.ToImmutableList();
            }
        }

        

        public virtual IReadOnlyList<IOnlineClient> GetAllByUserId(long userId)
        {
            return GetAllClients()
                 .Where(c => (c.UserId == userId))
                 .ToImmutableList();
        }


    }
}
